/******************************************************
 * @file rgb2xxx.c
 * @author Destiny 
 * @brief rgb转bmp、Gray
 * 
 * @version 0.1
 * @date 2024-07-18
 *****************************************************/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "render.h"
#include "config.h"
#include "cnn_operation.h"

#define IMAGE_WIDTH 320    // 图像宽度
#define IMAGE_HEIGHT 240   // 图像高度
#define RGB565_SIZE (IMAGE_WIDTH * IMAGE_HEIGHT * 2) // RGB565 图像数据大小

/* BMP文件头 */
typedef struct  { 
	uint16_t bfType; 
	unsigned long  bfSize;
	uint16_t bfReserved1;
	uint16_t bfReserved2;
	unsigned long  bfOffBits;
	unsigned long  biSize;
	unsigned long  biWidth;
	unsigned long  biHeight;
	uint16_t biPlanes;
	uint16_t biBitCount;
	unsigned long  biCompression;
	unsigned long  biSizeImage;
	unsigned long  biXPelsPerMeter;
	unsigned long  biYPelsPerMeter;
	unsigned long  biClrUsed;
	unsigned long  biClrImportant;
}  __attribute__ ((packed)) BMPHEADER;

/**
 * @brief RGB565转BMP，保存摄像头内容到本地
 * 
 * @param filename 保存路径
 * @param rgb565_data RGB565颜色数据
 * @param iWidth 宽
 * @param iHeight 高
 * @return int 成功：0;失败：其他
 */
static int RGB565ToBMP(const char *filename, uint16_t *rgb565_data,unsigned long iWidth,unsigned long iHeight) 
{
    BMPHEADER bmp_header = {
        .bfType = 0x4D42,          // "BM"
        .bfSize = iWidth * iHeight * 3 + sizeof(BMPHEADER),
        .bfOffBits = sizeof(BMPHEADER),
        .biSize = 40,
        .biWidth = iWidth,
        .biHeight = iHeight,
        .biPlanes = 1,
        .biBitCount = 24,    // RGB888 格式
        .biCompression = 0,
        .biSizeImage = iWidth * iHeight * 3,
        .biXPelsPerMeter = 0,
        .biYPelsPerMeter = 0,
        .biClrUsed = 0,
        .biClrImportant = 0
    };

    FILE *file = fopen(filename, "wb");
    if (!file) {
        DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"Error opening file\n");
        return -1;
    }
    fwrite(&bmp_header, sizeof(BMPHEADER), 1, file);

    uint8_t r,g,b,red,green,blue;
    uint16_t color;
    int i;
    for (i = iWidth * iHeight - 1; i >= 0; i--) 
    {
        color = rgb565_data[i];
        r = (color >> 11) & 0x1F;
        g = (color >> 5) & 0x3F;
        b = color & 0x1F;

        // 将 RGB565 转换为 RGB888
        red = (r * 255) / 31;
        green = (g * 255) / 63;
        blue = (b * 255) / 31;

        fwrite(&blue, 1, 1, file);
        fwrite(&green, 1, 1, file);
        fwrite(&red, 1, 1, file);
    }

    fclose(file);
    return 0;
}

/**
 * @brief RGB转Gray。
 *          近似计算： Gray=R∗0.299+G∗0.587+B∗0.114
 * @param ptPixelDatas 摄像头输入的图像数据
 * @param ptInoutputData 神经网络输入图像数据
 * @return int 成功：0;失败：其他
 */
static int RGBToGray(PT_PixelDatas ptPixelDatas,PT_InoutputData ptInoutputData)
{
    int iCurCol;      /*列*/  
	int iCurRow;     /*行*/
    uint8_t *pucFB;
	uint16_t *pwFB16bpp;
	//uint32_t *pdwFB32bpp;
	int iRed;
	int iGreen;
	int iBlue;
    int ucGray;
    pucFB      = ptPixelDatas->aucPixelDatas;
	pwFB16bpp  = (uint16_t *)pucFB;
    for (iCurRow = 0; iCurRow < ptInoutputData->iRow; iCurRow++)
    {
        for (iCurCol = 0; iCurCol < ptInoutputData->iCol; iCurCol++)
        {
            switch (ptPixelDatas->iBpp)
            {
                case 16:  /*RGB565*/
                {
                    iRed = (*pwFB16bpp >> 11) & 0x1f;
                    iGreen = (*pwFB16bpp >> 5) & 0x3f;
                    iBlue = *pwFB16bpp  & 0x1f;
                    //DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"RGB565:%4X\r\n",*pwFB16bpp);
                    pwFB16bpp++;
                    break;
                }
                case 24:  /*RGB888*/
                {
                    iRed   = *pucFB++;
                    iGreen = *pucFB++;
                    iBlue  = *pucFB++;
                    break;
                }
                case 32: /*RGB8888*/
                {
                    iRed   = *pucFB++;
                    iGreen = *pucFB++;
                    iBlue  = *pucFB++;
                    break;
                }
                default:
                {
                    return -1;
                }
            }
            ucGray = iRed * 0.3 + iGreen * 0.59 + iBlue * 0.11;
            ucGray = (ucGray > 15) ? 250 : 0;
            ptInoutputData->pppfDatas[0][iCurRow][iCurCol] = (float)ucGray/ 255;
           //DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"%d %d %d %d %.4f\r\n",iRed,iGreen,iBlue,ucGray,ptInoutputData->pppfDatas[0][iCurRow][iCurCol]);
        }
    }
    return 0;
}

/**
 * @brief 图像裁剪
 * 
 * @param ptOriginPic 原图片
 * @param ptDestPic   裁剪后的图片
 */
static void ImageCutting(PT_PixelDatas ptOriginPic, PT_PixelDatas ptDestPic)
{
    int i,j;
    uint8_t *pucSrc = ptOriginPic->aucPixelDatas;
    uint8_t *pucDest = ptDestPic->aucPixelDatas;
    for (i = 0; i < ptDestPic->iHeight; i++)
    {
        for (j = 0; j < ptDestPic->iLineBytes; j++)
        {
            pucSrc[j] = ~pucSrc[j];
        }
        memcpy(pucDest, pucSrc, ptDestPic->iLineBytes);
        pucDest += ptDestPic->iLineBytes;
        pucSrc += ptOriginPic->iLineBytes;
    }
}

/**
 * @brief 摄像头数据转成灰度图作为神经网络输入。
 * 
 * @param ptPixelDatas 摄像头输入的图像数据
 * @param ptInputData 神经网络输入图像数据
 * @return int 成功：0;失败：其他
 */
int PixelDataToCNNInputData(PT_PixelDatas ptPixelDatas,PT_InoutputData ptInputData)
{
    int iError;
    T_PixelDatas tTemp1,tTemp2;
    tTemp1.iBpp = ptPixelDatas->iBpp;
    tTemp1.iHeight = ptInputData->iRow * 10;
    tTemp1.iWidth = ptInputData->iCol * 10;
    tTemp1.iLineBytes = tTemp1.iWidth * tTemp1.iBpp / 8;
    tTemp1.iTotalBytes = tTemp1.iHeight * tTemp1.iLineBytes;
    tTemp1.aucPixelDatas = malloc(tTemp1.iTotalBytes);
    if (tTemp1.aucPixelDatas == NULL)
    {
        DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"can't malloc memory for tTemp1.aucPixelDatas! %s %s %d\r\n", __FILE__, __FUNCTION__, __LINE__);
        return -1;
    }
    ImageCutting(ptPixelDatas,&tTemp1);

    tTemp2.iBpp = ptPixelDatas->iBpp;
    tTemp2.iHeight = ptInputData->iRow ;
    tTemp2.iWidth = ptInputData->iCol ;
    tTemp2.iLineBytes = tTemp2.iWidth * tTemp2.iBpp / 8;
    tTemp2.iTotalBytes = tTemp2.iHeight * tTemp2.iLineBytes;
    tTemp2.aucPixelDatas = malloc(tTemp2.iTotalBytes);
    if (tTemp2.aucPixelDatas == NULL)
    {
        DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"can't malloc memory for tTemp2.aucPixelDatas! %s %s %d\r\n", __FILE__, __FUNCTION__, __LINE__);
        return -1;
    }
    PicZoom(&tTemp1,&tTemp2);

    iError = RGBToGray(&tTemp2, ptInputData);
    if (iError < 0)
    {
        DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"RGBToGray error! %s %s %d\r\n", __FILE__, __FUNCTION__, __LINE__);
        return -1;
    }
    free(tTemp1.aucPixelDatas);
    free(tTemp2.aucPixelDatas);
    return 0;
}

/**
 * @brief 以bmp图片保存图片
 * 
 * @param ptPixelDatas 摄像头的图像数据
 * @return int 成功：0;失败：其他
 */
int SavePixelDataAsBmp(PT_PixelDatas ptPixelDatas)
{
    char name[256];
    int iError;
    time_t current_time = time(NULL);
    if (current_time == ((time_t)-1)) 
    {
        DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"Error getting time\n");
        return -1;
    }
    snprintf(name,256,"%s/%ld%s",IMAGE_DIR,current_time,".bmp");
    name[255] = '\0';
    iError = RGB565ToBMP(name,(uint16_t *)ptPixelDatas->aucPixelDatas,ptPixelDatas->iWidth,ptPixelDatas->iHeight);
    if(iError < 0)
    {
        DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"Rgb565ToBMP error!\n");
        return -1;
    }
    DBG_PRINTF(DLOG_LVL_INFO,DLOG_TAG,"save image %s success\n",name);
    return 0;
}